package cn.edu.hitsz.compiler.parser;

import cn.edu.hitsz.compiler.NotImplementedException;
import cn.edu.hitsz.compiler.lexer.Token;
import cn.edu.hitsz.compiler.parser.table.*;
import cn.edu.hitsz.compiler.symtab.SymbolTable;

import java.util.*;

//TODO: 实验二: 实现 LR 语法分析驱动程序

/**
 * LR 语法分析驱动程序
 * <br>
 * 该程序接受词法单元串与 LR 分析表 (action 和 goto 表), 按表对词法单元流进行分析, 执行对应动作, 并在执行动作时通知各注册的观察者.
 * <br>
 * 你应当按照被挖空的方法的文档实现对应方法, 你可以随意为该类添加你需要的私有成员对象, 但不应该再为此类添加公有接口, 也不应该改动未被挖空的方法,
 * 除非你已经同助教充分沟通, 并能证明你的修改的合理性, 且令助教确定可能被改动的评测方法. 随意修改该类的其它部分有可能导致自动评测出错而被扣分.
 */
public class SyntaxAnalyzer {
    private final SymbolTable symbolTable;
    private final List<ActionObserver> observers = new ArrayList<>();
    private final List<Token> tokens = new ArrayList<>();
    private LRTable lrTable;

    public SyntaxAnalyzer(SymbolTable symbolTable) {
        this.symbolTable = symbolTable;
    }

    /**
     * 注册新的观察者
     *
     * @param observer 观察者
     */
    public void registerObserver(ActionObserver observer) {
        observers.add(observer);
        observer.setSymbolTable(symbolTable);
    }

    /**
     * 在执行 shift 动作时通知各个观察者
     *
     * @param currentStatus 当前状态
     * @param currentToken  当前词法单元
     */
    public void callWhenInShift(Status currentStatus, Token currentToken) {
        for (final var listener : observers) {
            listener.whenShift(currentStatus, currentToken);
        }
    }

    /**
     * 在执行 reduce 动作时通知各个观察者
     *
     * @param currentStatus 当前状态
     * @param production    待规约的产生式
     */
    public void callWhenInReduce(Status currentStatus, Production production) {
        for (final var listener : observers) {
            listener.whenReduce(currentStatus, production);
        }
    }

    /**
     * 在执行 accept 动作时通知各个观察者
     *
     * @param currentStatus 当前状态
     */
    public void callWhenInAccept(Status currentStatus) {
        for (final var listener : observers) {
            listener.whenAccept(currentStatus);
        }
    }

    public void loadTokens(Iterable<Token> tokens) {
        Iterator<Token> tokens_iterator = tokens.iterator();
        while(tokens_iterator.hasNext())
        {
            this.tokens.add(tokens_iterator.next());
        }
    }

    public void loadLRTable(LRTable table) {
        lrTable = table;
    }

    public void run() {
        LinkedList<Status> status = new LinkedList<>();
        status.add(lrTable.getInit());
        int pos = 0;
        while(pos < tokens.size())
        {
            Token token = tokens.get(pos);
            Status now = status.getLast();
            Action action = lrTable.getAction(now, token);
            switch (action.getKind())
            {
                case Shift:
                {
                    callWhenInShift(now, token);
                    status.add(action.getStatus());
                    pos++;
                    break;
                }
                case Reduce:
                {
                    Production production = action.getProduction();
                    callWhenInReduce(now, production);
                    List<String> bodyStr = Arrays.stream(production.toString().split(" ")).toList();
                    for(int i=0;i<bodyStr.size()-2;i++)
                    {
                        status.removeLast();
                    }
                    status.add(lrTable.getGoto(status.getLast(), new NonTerminal(bodyStr.get(0))));
                    break;
                }
                case Accept:
                {
                    callWhenInAccept(now);
                    pos++;
                    return ;
                }
                case Error:
                {
                    throw new RuntimeException("语法分析出错");
                }
            }
        }
    }
}
